#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>  // shared memory
#include<sys/sem.h>  // semaphore
#include<sys/msg.h>  // message queue
#include<string.h>   // memcpy

// 消息队列结构
struct msg_form {
  long mtype;
  char mtext;
};

// 联合体，用于semctl初始化
union semun
{
  int              val; /*for SETVAL*/
  struct semid_ds *buf;
  unsigned short  *array;
};

// P操作:
//  若信号量值为1，获取资源并将信号量值-1
//  若信号量值为0，进程挂起等待
int sem_p(int sem_id)
{
  struct sembuf sbuf;
  sbuf.sem_num = 0; /*序号*/
  sbuf.sem_op = -1; /*P操作*/
  sbuf.sem_flg = SEM_UNDO;

  if(semop(sem_id, &sbuf, 1) == -1)
  {
    perror("P operation Error");
    return -1;
  }
  return 0;
}

// V操作：
//  释放资源并将信号量值+1
//  如果有进程正在挂起等待，则唤醒它们
int sem_v(int sem_id)
{
  struct sembuf sbuf;
  sbuf.sem_num = 0; /*序号*/
  sbuf.sem_op = 1;  /*V操作*/
  sbuf.sem_flg = SEM_UNDO;

  if(semop(sem_id, &sbuf, 1) == -1)
  {
    perror("V operation Error");
    return -1;
  }
  return 0;
}


int main()
{
  key_t key;
  int shmid, semid, msqid;
  char *shm;
  struct msg_form msg;
  int flag = 1; /*while循环条件*/

  // 获取key值
  if((key = ftok(".", 'z')) < 0)
  {
    perror("ftok error");
    exit(1);
  }

  // 获取共享内存
  if((shmid = shmget(key, 1024, 0)) == -1)
  {
    perror("shmget error");
    exit(1);
  }

  // 连接共享内存
  shm = (char*)shmat(shmid, 0, 0);
  if((int)shm == -1)
  {
    perror("Attach Shared Memory Error");
    exit(1);
  }

  // 创建消息队列
  if ((msqid = msgget(key, 0)) == -1)
  {
    perror("msgget error");
    exit(1);
  }

  // 获取信号量
  if((semid = semget(key, 0, 0)) == -1)
  {
    perror("semget error");
    exit(1);
  }

  // 写数据
  printf("***************************************\n");
  printf("*                 IPC                 *\n");
  printf("*    Input r to send data to server.  *\n");
  printf("*    Input q to quit.                 *\n");
  printf("***************************************\n");

  while(flag)
  {
    char c;
    printf("Please input command: ");
    scanf("%c", &c);
    switch(c)
    {
      case 'r':
      printf("Data to send: ");
      sem_p(semid);  /*访问资源*/
      scanf("%s", shm);
      sem_v(semid);  /*释放资源*/
      /*清空标准输入缓冲区*/
      while((c=getchar())!='\n' && c!=EOF);
      msg.mtype = 888;
      msg.mtext = 'r';  /*发送消息通知服务器读数据*/
      msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
      break;
      case 'q':
      msg.mtype = 888;
      msg.mtext = 'q';
      msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
      flag = 0;
      break;
      default:
      printf("Wrong input!\n");
      /*清空标准输入缓冲区,当scanf()输入字符或字符串时，缓冲区中遗留下了\n，所以每次输入操作后都需要清空标准输入的缓冲区*/
      while((c=getchar())!='\n' && c!=EOF);
    }
  }

  // 断开连接
  shmdt(shm);

  return 0;
}
//原文：https://blog.csdn.net/wh_sjc/article/details/70283843
